package tagdb.io;

import tagdb.algorithm.match.PeptideSpectrumMatch;
import tagdb.denovo.DenovoHit;
import tagdb.model.PeptideHit;
import tagdb.model.ProteinFamilyHit;
import tagdb.model.ProteinHit;
import tagdb.model.TagSearchResult;
import tagdb.util.Formatter;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

/**
 * <b>ResultExporter</b> <p>This class holds basic export functions.</p>
 *
 * @author T.Muth
 *
 */
public class ResultExporter {

    /**
     * Comma / semi-colon separated format.
     */
    private static final String SEP = ";";

    /**
     * The different export types.
     */
    public enum ExportType {

        PROTEINS, PEPTIDES, PSMS, SCORES
    }

    /**
     * This method exports the protein results.
     *
     * @param filePath The file path to the exported file.
     * @param result The search result.
     * @throws IOException Exception thrown when the file access fails.
     */
    public static void exportProteins(String filePath, TagSearchResult result) throws IOException {
        // Init the buffered writer.
        BufferedWriter writer = new BufferedWriter(new FileWriter(new File(filePath)));
        try {

            // Protein format.
            writer.append(getProteinHeader());
            writer.newLine();

            int count = 1;

            for (Entry e1 : result.getResultMap().entrySet()) {

                // Get the protein hit.
                ProteinFamilyHit proteinFamilyHit = (ProteinFamilyHit) e1.getValue();
                writer.append(count + SEP);
                writer.append(proteinFamilyHit.getName() + SEP);
                writer.append(proteinFamilyHit.getUniqueSpectralCount() + SEP);

                // Protein Index
                int proteinCount = 1;
                Set<Entry<String, ProteinHit>> proteins = proteinFamilyHit.getProteinHits().entrySet();

                for (Entry e2 : proteins) {
                    ProteinHit proteinHit = (ProteinHit) e2.getValue();
                    writer.append(proteinCount + SEP);
                    writer.append(proteinHit.getAccession() + SEP);
                    writer.append(proteinHit.getDescription() + SEP);
                    writer.append(proteinHit.getSpecies() + SEP);
                    writer.append(proteinHit.getMolWeight() + SEP);
                    writer.newLine();
                    if (proteins.size() != proteinCount) {
                        writer.append(SEP);
                        writer.append(SEP);
                        writer.append(SEP);
                    }
                    proteinCount++;
                }
                count++;
                writer.flush();
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            writer.close();
        }
    }

    /**
     * This method exports the peptide results.
     *
     * @param filePath The file path to the exported file.
     * @param result The search result.
     * @throws IOException Exception thrown when the file access fails.
     */
    public static void exportPeptides(String filePath, TagSearchResult result) throws IOException {
        // Init the buffered writer.
        BufferedWriter writer = new BufferedWriter(new FileWriter(new File(filePath)));
        try {

            int count = 1;

            // Peptide header.
            writer.append(getPeptideHeader());
            writer.newLine();

            for (Entry e1 : result.getResultMap().entrySet()) {

                // Get the protein hit.
                ProteinFamilyHit proteinFamilyHit = (ProteinFamilyHit) e1.getValue();
                Set<Entry<String, ProteinHit>> proteins = proteinFamilyHit.getProteinHits().entrySet();

                for (Entry e2 : proteins) {
                    ProteinHit proteinHit = (ProteinHit) e2.getValue();

                    for (PeptideHit peptideHit : proteinHit.getPeptideHitList()) {
                        writer.append(count++ + SEP);
                        writer.append(peptideHit.getSequence() + SEP);
                        writer.append(peptideHit.getSpectrumMatches().size() + SEP);
                        writer.append(proteinHit.getAccession() + SEP);
                        writer.append(proteinHit.getDescription() + SEP);
                        writer.newLine();
                    }
                }
                writer.flush();
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            writer.close();
        }
    }

    /**
     * This method exports the PSM results.
     *
     * @param filePath The file path to the exported file.
     * @param result The search result.
     * @throws IOException Exception thrown when the file access fails.
     */
    public static void exportPSMs(String filePath, TagSearchResult result) throws IOException {
        // Init the buffered writer.
        BufferedWriter writer = new BufferedWriter(new FileWriter(new File(filePath)));
        try {

            int count = 1;

            // Peptide header.
            writer.append(getPSMHeader());
            writer.newLine();

            for (Entry e1 : result.getResultMap().entrySet()) {

                // Get the protein hit.
                ProteinFamilyHit proteinFamilyHit = (ProteinFamilyHit) e1.getValue();
                Set<Entry<String, ProteinHit>> proteins = proteinFamilyHit.getProteinHits().entrySet();

                for (Entry e2 : proteins) {
                    ProteinHit proteinHit = (ProteinHit) e2.getValue();

                    for (PeptideHit peptideHit : proteinHit.getPeptideHitList()) {

                        // PSM results
                        for (PeptideSpectrumMatch psm : peptideHit.getSpectrumMatchesList()) {
                            writer.append(count++ + SEP);
                            writer.append(psm.getSpectrumTitle() + SEP);
                            writer.append(peptideHit.getSequence() + SEP);
                            writer.append(proteinHit.getAccession() + SEP);
                            writer.append(Formatter.roundDouble(psm.getIntensityScore(), 3) + SEP);
                            writer.append(psm.getIntensityScore() + SEP);
                            writer.append(psm.getMatchedPeaks() + SEP);
                            writer.newLine();
                        }
                    }
                }
                writer.flush();
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            writer.close();
        }
    }

    /**
     * This method exports the PSM scores.
     *
     * @param filePath The file path to the exported file.
     * @param resultsMap The search result.
     * @throws IOException Exception thrown when the file access fails.
     */
    public static void exportScores(String filePath, Map<String, List<DenovoHit>> resultsMap) throws IOException {
        // Init the buffered writer.
        BufferedWriter writer = new BufferedWriter(new FileWriter(new File(filePath)));
        try {

            int count = 1;

            // header
            writer.append(getScoreHeader());
            writer.newLine();

            for (String spectrum : resultsMap.keySet()) {

                for (DenovoHit denovoHit : resultsMap.get(spectrum)) {

                    // Get the protein hit.
                    writer.append(spectrum + SEP);
                    writer.append(denovoHit.getIndex() + SEP);
                    writer.append(denovoHit.getSequence() + SEP);
                    writer.append(denovoHit.getLength() + SEP);
                    writer.append(denovoHit.getCharge() + SEP);
                    writer.append(denovoHit.getNTermGap() + SEP);
                    writer.append(denovoHit.getCTermGap() + SEP);
                    writer.append(denovoHit.getPepNovoScore() + SEP);
                    writer.append(denovoHit.getRankScore() + SEP);
                    writer.newLine();
                    writer.flush();
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            writer.close();
        }
    }

    /**
     * Returns the protein header string.
     *
     * @return The protein header string.
     */
    private static String getProteinHeader() {
        return "Protein Family No." + SEP
                + "Protein Family Name" + SEP
                + "Spectral Count" + SEP
                + "Protein No." + SEP
                + "Accession" + SEP
                + "Description" + SEP
                + "Species" + SEP
                + "MW [kDa]" + SEP;
    }

    /**
     * Returns the peptide header string.
     *
     * @return The peptide header string.
     */
    private static String getPeptideHeader() {
        return "Spectrum" + SEP
                + "Peptide Sequence" + SEP
                + "No. Peptide Spectrum Matches" + SEP
                + "Protein Accession" + SEP
                + "Protein Description" + SEP;
    }

    /**
     * Returns the PSM header string.
     *
     * @return The PSM header string.
     */
    private static String getPSMHeader() {
        return "PSM No." + SEP
                + "Spectrum title" + SEP
                + "Peptide Sequence" + SEP
                + "Protein Accession" + SEP
                + "Intensity Score" + SEP
                + "Matched Peaks" + SEP;
    }

    /**
     * Returns the score header string.
     *
     * @return The score header string.
     */
    private static String getScoreHeader() {
        return "PSM No." + SEP
                + "Rank" + SEP
                + "sequence" + SEP
                + "length" + SEP
                + "charge" + SEP
                + "N-term gap" + SEP
                + "C-term gap" + SEP
                + "Pepnovo score" + SEP
                + "rank score" + SEP;
    }
}
